/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.extension.index.security;

import java.util.HashSet;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.metaeffekt.core.security.cvss.CvssVector;
import org.xwiki.extension.version.Version;
import org.xwiki.text.XWikiToStringBuilder;

/**
 * An individual security vulnerability descriptor.
 *
 * @version $Id$
 * @since 15.5RC1
 */
public class SecurityVulnerabilityDescriptor
{
    private String id;

    private Set<String> aliases;

    private String url;

    private double score;

    private Version fixVersion;

    private boolean safe;

    private String reviews;

    /**
     * @param id the security vulnerability id, by default a CVE id, or another id if no CVE is found
     * @return the current object
     */
    public SecurityVulnerabilityDescriptor setId(String id)
    {
        this.id = id;
        return this;
    }

    /**
     * @return the security vulnerability id, by default a CVE id, or another id if no CVE is found
     */
    public String getId()
    {
        return this.id;
    }

    /**
     * @param url an external URL providing more details on the vulnerability
     * @return the current object
     */
    public SecurityVulnerabilityDescriptor setURL(String url)
    {
        this.url = url;
        return this;
    }

    /**
     * @return an external URL providing more details on the vulnerability
     */
    public String getURL()
    {
        return this.url;
    }

    /**
     * Compute and store the score from the provided CVSS vector.
     *
     * @param vector a CVSS vector to parse and compute the based score from
     * @return the current object
     * @see #getScore()
     */
    public SecurityVulnerabilityDescriptor setSeverityScore(String vector)
    {
        if (StringUtils.isNotEmpty(vector)) {
            this.score = CvssVector.parseVector(vector).getBaseScore();
        }
        return this;
    }

    /**
     * @return the CVSS score of the security vulnerability
     */
    public double getScore()
    {
        return this.score;
    }

    /**
     * @param score the CVSS score of the security vulnerability
     * @return the current object
     */
    public SecurityVulnerabilityDescriptor setScore(double score)
    {
        this.score = score;
        return this;
    }

    /**
     * @return the minimal version to which to upgrade to get the issue fixed automatically
     */
    public Version getFixVersion()
    {
        return this.fixVersion;
    }

    /**
     * @param fixVersion the minimal version to which to upgrade to get the issue fixed automatically
     * @return the current object
     */
    public SecurityVulnerabilityDescriptor setFixVersion(Version fixVersion)
    {
        this.fixVersion = fixVersion;
        return this;
    }

    /**
     * @return {@code true} when the extension has some known vulnerabilities, but all are reviewed as safe,
     *     {@code false} otherwise
     * @since 15.6RC1
     */
    public boolean isSafe()
    {
        return this.safe;
    }

    /**
     * @param safe {@code true} when the extension has some known vulnerabilities, but all are reviewed as safe,
     *     {@code false} otherwise
     * @return the current object
     * @since 15.6RC1
     */
    public SecurityVulnerabilityDescriptor setSafe(boolean safe)
    {
        this.safe = safe;
        return this;
    }

    /**
     * @return the ignored vulnerabilities description, this is an html content containing all the false-positive
     *     analysis
     * @since 15.6RC1
     */
    public String getReviews()
    {
        return this.reviews;
    }

    /**
     * @param reviews the ignored vulnerabilities description, this is an html content containing all the
     *     false-positive analysis
     * @return the current object
     * @since 15.6RC1
     */
    public SecurityVulnerabilityDescriptor setReviews(String reviews)
    {
        this.reviews = reviews;
        return this;
    }

    /**
     * Retrieves the set of aliases associated with the current object.
     *
     * @return a set of aliases, used for vulnerability comparison, two vulnerabilities sharing an alias or an id are
     *     considered equals
     * @since 15.9RC1
     * @since 15.5.4
     */
    public Set<String> getAliases()
    {
        if (this.aliases == null) {
            this.aliases = new HashSet<>();
        }
        return this.aliases;
    }

    /**
     * Sets the set of aliases associated with the current object.
     *
     * @param aliases a set of aliases, used for vulnerability comparison, two vulnerabilities sharing an alias or
     *     an id are considered equals
     * @return the current object
     * @since 15.9RC1
     * @since 15.5.4
     */
    public SecurityVulnerabilityDescriptor setAliases(Set<String> aliases)
    {
        this.aliases = aliases;
        return this;
    }

    @Override
    public boolean equals(Object o)
    {
        if (this == o) {
            return true;
        }

        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        SecurityVulnerabilityDescriptor that = (SecurityVulnerabilityDescriptor) o;

        return new EqualsBuilder()
            .append(this.score, that.score)
            .append(this.id, that.id)
            .append(this.url, that.url)
            .append(this.fixVersion, that.fixVersion)
            .append(this.safe, that.safe)
            .append(this.reviews, that.reviews)
            .append(this.aliases, that.aliases)
            .isEquals();
    }

    @Override
    public int hashCode()
    {
        return new HashCodeBuilder(17, 37)
            .append(this.id)
            .append(this.url)
            .append(this.score)
            .append(this.fixVersion)
            .append(this.safe)
            .append(this.reviews)
            .append(this.aliases)
            .toHashCode();
    }

    @Override
    public String toString()
    {
        return new XWikiToStringBuilder(this)
            .append("id", this.id)
            .append("url", this.url)
            .append("score", this.score)
            .append("fixVersion", this.fixVersion)
            .append("safe", this.safe)
            .append("reviews", this.reviews)
            .append("aliases", this.aliases)
            .toString();
    }
}
